package com.yerbamateprimer.action.user.impl;

import java.io.Serializable;

import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

import com.google.common.base.Preconditions;
import com.yerbamateprimer.action.user.AsynchronousUserAction;
import com.yerbamateprimer.model.Mate;
import com.yerbamateprimer.model.User;
import com.yerbamateprimer.round.Round;
import com.yerbamateprimer.round.state.State;
import com.yerbamateprimer.round.state.StateName;
import com.yerbamateprimer.service.RoundService;

/**
 * This action is useful to set
 * the mate properties at the 
 * yerba mate round.
 * 
 * @author Matias Liwski
 * @version 1.0
 */
public class SetMateUserAction implements AsynchronousUserAction, Serializable {
	private static final long serialVersionUID = -1333318181031049644L;
	private User user;
	private Mate mate;
	private transient RoundService roundService;

	/**
	 * Default constructor, used by the serializer.
	 */
	public SetMateUserAction() {
	}

	/**
	 * @param user The user that want to execute this action.
	 * @param mate The yerba mate to set.
	 */
	public SetMateUserAction(User user, Mate mate) {
		super();
		setUser(user);
		setMate(mate);
	}

	/* (non-Javadoc)
	 * @see com.yerbamateprimer.action.user.UserAction#execute(com.yerbamateprimer.service.RoundService, com.yerbamateprimer.service.UserService)
	 */
	@Override
	public void execute() {
		checkArgumentPreconditions();
		//Check action needed preconditions
		Round round = this.roundService.getRoundForUser(this.user);
		Preconditions.checkState(round != null, "The user: " + this.user + " isn't participating at any round.");
		User primer = round.getPrimer();
		Preconditions.checkState(this.user.equals(primer), "Only the primer could set the mate ( user: " + this.user + ", primer: " + primer + " ).");
		State currentState = round.getCurrentState();
		Preconditions.checkState(StateName.BUILD.equals(currentState.getName()), "Couldn't set the mate after the BUILDING state, current state: " + currentState + ".");
		
		round.setMate(this.mate);
	}

	/**
	 * Check the preconditions of the arguments needed to execute this action. 
	 */
	private void checkArgumentPreconditions() {
		Preconditions.checkArgument(this.user != null, "This action need a user to execute it.");
		Preconditions.checkArgument(roundService != null, "This action need a round service to execute it.");
		Preconditions.checkArgument(this.mate != null, "This action need a mate to execute it.");
	}

	/**
	 * @param mate The mate to set.
	 */
	public void setMate(Mate mate) {
		Preconditions.checkArgument(mate != null, "This action need a mate to execute it.");
		this.mate = mate;
	}

	/**
	 * @return The mate.
	 */
	public Mate getMate() {
		return mate;
	}

	/* (non-Javadoc)
	 * @see com.yerbamateprimer.action.user.UserAction#setUser(com.yerbamateprimer.model.User)
	 */
	@Override
	public void setUser(User user) {
		Preconditions.checkArgument(user != null, "This action needs a user to execute it.");
		this.user = user;
	}

	/* (non-Javadoc)
	 * @see com.yerbamateprimer.action.user.UserAction#getUser()
	 */
	@Override
	public User getUser() {
		return this.user;
	}

	/**
	 * @param roundService The round service of the Action, to set.
	 */
	public void setRoundService(RoundService roundService) {
		Preconditions.checkArgument(roundService != null, "This action needs a round service to execute it.");
		this.roundService = roundService;
	}

	/**
	 * @return The roundService of the Action.
	 */
	public RoundService getRoundService() {
		return this.roundService;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE, false, true);
	}
}